package org.xiaog.fastdao.util;

import com.alibaba.druid.util.StringUtils;
import org.xiaog.fastdao.ResultMapping;
import org.xiaog.fastdao.annotation.Table;
import org.xiaog.fastdao.annotation.TableColumn;
import org.xiaog.fastdao.annotation.TableId;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xiaog
 */
public class EntityMeta {

    private static final Map<Class<?>,EntityMeta> MAP = new ConcurrentHashMap<>();

    private String tableName;

    private String idField;

    private String idColumn;

    private Class<?> idType;

    private List<String> columns;

    private List<String> fields;

    private List<Class<?>> fieldsType;

    private EntityMeta() {}

    static class DynamicEntity {
        private String idCol;
        private Object idVal;
        private List<String> otherCol;
        private List<Object> otherVal;

        public String getIdCol() {
            return idCol;
        }

        public void setIdCol(String idCol) {
            this.idCol = idCol;
        }

        public Object getIdVal() {
            return idVal;
        }

        public void setIdVal(Object idVal) {
            this.idVal = idVal;
        }

        public List<String> getOtherCol() {
            return otherCol;
        }

        public void setOtherCol(List<String> otherCol) {
            this.otherCol = otherCol;
        }

        public List<Object> getOtherVal() {
            return otherVal;
        }

        public void setOtherVal(List<Object> otherVal) {
            this.otherVal = otherVal;
        }
    }

    public static DynamicEntity getNotNullColumns(Object entity) {
        Class<?> eClass = entity.getClass();
        EntityMeta eMeta = EntityMeta.getInstance(eClass);
        List<String> otherFields = eMeta.fields;
        String idField = eMeta.idField;
        DynamicEntity dynamic = new DynamicEntity();
        dynamic.setOtherCol(new ArrayList<>());
        dynamic.setOtherVal(new ArrayList<>());
        try {
            Field idF = eClass.getDeclaredField(idField);
            idF.setAccessible(true);
            dynamic.setIdCol(eMeta.getIdColumn());
            Object idV = idF.get(entity);
            dynamic.setIdVal(idV);
            for (String otherField : otherFields) {
                Field of = eClass.getDeclaredField(otherField);
                of.setAccessible(true);
                Object oo = of.get(entity);
                if (oo != null) {
                    dynamic.getOtherCol().add(eMeta.getColumn(otherField));
                    dynamic.getOtherVal().add(oo);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dynamic;
    }

    public static EntityMeta getInstance(Class<?> eClass) {
        EntityMeta eMeta = MAP.get(eClass);
        if (eMeta == null) {
            eMeta = create(eClass);
            MAP.put(eClass, eMeta);
        }
        return eMeta;
    }

    private static EntityMeta create(Class<?> eClass) {
        EntityMeta eMeta = new EntityMeta();
        Table table = eClass.getAnnotation(Table.class);
        if (table != null && !StringUtils.isEmpty(table.value())) {
            eMeta.tableName = table.value();
        }else {
            String name = eClass.getSimpleName();
            eMeta.tableName = ResultMapping.underline(name);
        }
        Field[] fields = eClass.getDeclaredFields();
        eMeta.columns = new ArrayList<>(fields.length);
        eMeta.fields = new ArrayList<>(fields.length);
        eMeta.fieldsType = new ArrayList<>(fields.length);
        for (Field field : fields) {
            TableColumn tableColumn = field.getAnnotation(TableColumn.class);
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) {
                String id = tableId.value();
                String fieldName = field.getName();
                if (StringUtils.isEmpty(id)) {
                    id = ResultMapping.underline(fieldName);
                }
                eMeta.idColumn = id;
                eMeta.idField = fieldName;
                eMeta.idType = field.getType();
            }else {
                String fieldName = field.getName();
                eMeta.fields.add(fieldName);
                eMeta.fieldsType.add(field.getType());
                if (tableColumn != null && !StringUtils.isEmpty(tableColumn.value())) {
                    eMeta.columns.add(tableColumn.value());
                }else {
                    String columnName = ResultMapping.underline(fieldName);
                    eMeta.columns.add(columnName);
                }
            }
        }
        return eMeta;
    }

    public String getTableName() {
        return tableName;
    }

    public String getIdField() {
        return idField;
    }

    public String getIdColumn() {
        return idColumn;
    }

    public List<String> getColumns() {
        return columns;
    }

    public List<String> getFields() {
        return fields;
    }

    public String getField(String column) {
        return fields.get(columns.indexOf(column));
    }

    public String getColumn(String field) {
        return columns.get(fields.indexOf(field));
    }

    public Class<?> getIdType() {
        return idType;
    }

    public List<Class<?>> getFieldsType() {
        return fieldsType;
    }
}
